﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Net;
using System.Windows.Forms;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Diagnostics;


namespace HttpDownloader
{
    class SegmentThread
    {
        int idxThread;
        String link;
        String request;
        private string m_linkRequest = "";
        private string m_host = "";
        private Int64 m_begin = 0;
        private Int64 m_end = 0;
        public bool goPause;
        public bool isThreadFail = false;
        Int64 iOffset;

        Int64 iCurrentDownloaded = 0;
        public int iThreadFail = 0;
        public int iTotalThread = 0;
        public string m_pInfo = "";
        public Socket client;
        public IPEndPoint m_iep;
        public bool isFinish = false;
        BinaryWriter m_pWrite;
        public FileStream m_pFileStream;
        string m_pFilePath;

        public bool isCheck = false;//kiem tra xem luong download nay da ktra chua
        private Int64 iRecvByte = 0;
        

        public Int64 ICurrentDownloaded
        {
            get { return iCurrentDownloaded; }
            set
            {
                this.iCurrentDownloaded = value;
            }
        }
        public event EventHandler OnReceiveData;
        public Int64 IRecvByte
        {
            get { return iRecvByte; }
            set
            {
                iRecvByte = value;
                if (this.OnReceiveData != null)
                    this.OnReceiveData(this, new EventArgs());
            }
        }

        public SegmentThread()
        {

        }

        public void setFileStream(ref FileStream fStream)
        {
            m_pFileStream = fStream;
            m_pWrite = new BinaryWriter(m_pFileStream);
        }

        public SegmentThread(int index, string trueLink)
        {
            link = trueLink;
            idxThread = index;
            isFinish = false;
            goPause = false;
        }
        
        public void  setRequest(string linkRequest,string host,Int64 begin, Int64 end, IPEndPoint iep)
        {
            m_linkRequest = linkRequest;
            m_host = host;
            m_begin = begin;
            m_end = end;
            m_iep = iep; //thiep lap IPEndpoint
        }

        //tao mot delegate gui thong diep ra ngoai
        public delegate void ReadStreamHandler();

        public void DoStart(object startPoint)
        {
            Int64 iStartPoint = 0;
            byte[] requestData = new byte[1024];
            byte[] data = new byte[1024];
            int recv = 0;
            string host = GetHost(link);
            string address = GetIPAdress(host);

            try
            {
                client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                try
                {
                    iStartPoint = Convert.ToInt64(startPoint);
                    client.Connect(m_iep);
                }
                catch (Exception)
                {
                    MessageBox.Show("Connect fail");
                    return;
                }

                string linkRequest = link.Substring(7 + host.Length);

                //gui yeu cau
                request = "GET " + m_linkRequest + " HTTP/1.1\r\nHost:" + host + 
                                                            "\r\nUser_Agent: Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 6.0)"+
                                                            "\r\nRange:bytes=" + (m_begin + iStartPoint) + "-" + m_end +
                                                            "\r\nUser_Agent: Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 6.0)" +
                                                            "\r\nCache-Control: no-cache" +
                                                            "\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" +
                                                            "\r\nAccept-Language: vi-vn,vi;q=0.8,en-us;q=0.5,en;q=0.3" +
                                                            "\r\nAccept-Encoding: gzip,deflate" +
                                                            "\r\nAccept-Charset: UTF-8,*" +
                                                            "\r\nKeep-Alive: 300" +
                                                            "\r\n\r\n";

                requestData = Encoding.ASCII.GetBytes(request);
                int iSend = client.Send(requestData, requestData.Length, SocketFlags.None);

                string strTempData = ""; // bien luu du lieu thua khi cat fan header
                string strData = "";
                string line = "";
                StringReader strReader;
                bool isHeader = true;
                byte[] tembyte = new byte[1024];

                int total = 0;
                while ((recv = client.Receive(data)) != 0)
                {
                    tembyte = new byte[recv];
                    for (int i = 0; i < recv; i++)
                    {
                        tembyte[i] = data[i];
                    }
                    strData = Encoding.ASCII.GetString(tembyte);
                    strReader = new StringReader(strData);
                    total += recv;
                    while ((line = strReader.ReadLine()) != null)
                    {
                        if (line.Equals(""))
                        {
                            isHeader = false;
                        }
                        else if (!isHeader)
                        {
                            strTempData = strTempData + line;
                            line = "";
                        }
                    }
                    if (!isHeader)
                    {
                        break;
                    }
                }

                data = new byte[1024*1024];

                if (!isThreadFail) 
                {
                    m_pFileStream = new FileStream(m_pFilePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write);
                    iOffset = m_begin + iStartPoint;
                }

                ASCIIEncoding acii = new ASCIIEncoding();
                m_pWrite = new BinaryWriter(m_pFileStream, acii);
                m_pWrite.Seek((int)iOffset, SeekOrigin.Begin);

                if (strTempData.Length != 0)
                {
                    m_pWrite.Write(Encoding.ASCII.GetBytes(strTempData));
                }
                
                int iRecvData = 0;

                recv = 0;
                while ((recv = client.Receive(data)) > 0)
                {
                    this.IRecvByte = recv;//bien nay dung de khi nhan du lieu se thong bao ra ngoai de cap nhat

                    iRecvData += recv;
                    this.ICurrentDownloaded = iRecvData;

                    m_pInfo = "Downloading...";
                    for (int i = 0; i < recv; i++)
                    {
                        m_pWrite.Write(data[i]);

                    }
                    recv = 0;
                    data = new byte[1024*1024];
                    if (goPause == true)
                    {
                        break;
                    }
                }

                if (!goPause)
                {
                    this.isFinish = true;
                    m_pInfo = "Downloaded";
                }
            }
            catch (System.Exception ex)
            {
                Debug.WriteLine("Loi");
                Debug.Write("Loi la: "+ex.ToString());
                string err = ex.ToString();
                if (err.Contains("An existing connection was forcibly closed by the remote host"))
                {
                    isThreadFail = true;
                    m_pInfo = "Disconnect";
                    while (true)
                    {
                        Thread.Sleep(2000);
                        iOffset = m_begin + iCurrentDownloaded;
                        DoStart(iCurrentDownloaded);
                        if (isFinish)
                        {
                            break;
                        }
                        if (iThreadFail >= iTotalThread)
                        {
                            this.isFinish = true;
                            break;
                        }
                    }
                }
            }
            finally
            {
                
                client.Close();
            }       
        }

        public static byte[] StrToByteArray(string str)
        {
            System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
            return encoding.GetBytes(str);
        }

        public static string ByteArrayToStr(byte[] byteArray)
        {
            System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
            return encoding.GetString(byteArray);
        }

        public void setFileName(string filepath)
        {
            m_pFilePath = filepath;
        }

        public string getInfo()
        {
            return m_pInfo;
        }

        public void setInfo(string info)
        {
            m_pInfo = info;
        }

        public void closeFile()
        {
            m_pFileStream.Close();
            m_pWrite.Close();
        }

        internal string GetIPAdress(string host)
        {
            IPHostEntry hostInfo = Dns.GetHostEntry(host);  
            IPAddress[] address = hostInfo.AddressList;
            return address[0].ToString();
        }

        internal string GetHost(string url)
        {
            url = url.ToLower();
            Uri tmp = new Uri(url);
            return tmp.Host;
        }

        public int IdxThread
        {
            get { return idxThread; }
            set { idxThread = value; }
        }

        public String Link
        {
            get { return link; }
            set { link = value; }
        }

        public Int64 IOffset
        {
            get { return iOffset; }
            set { iOffset = value; }
        }
    }
}
